<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>files-preview</title>
  <style src="./index.css"></style>
  <meta name="referrer" content="never">
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
</head>

<body>
  <div id="app">
    <header :class="['header-wrap',{blurHeder:blurHeder}]">
      <div class="header">
        <div class="logo">FILES PREVIEW</div>
      </div>
    </header>
    <div class="hero"
      style="background-image: url('https://source.boringavatars.com/marble/440/?square&color=2f54eb,f5222d,ffc53d,#13c2c2,1890ff')">
    </div>
    <main class="content" ref="content">

      <h2 class="title">
        <a :href="path[path.length-2].path" class="back-icon" v-if="path && path.length >= 2"></a>
        <span>{{currentName == '/' ? '项目总览': currentName || '项目总览' }}</span>
      </h2>
      <p class="path">
        快速导航 :
        <template v-for="(item,index) in path">
          <a :href="item.path" :key="index">{{item.text || 'Home'}}</a>
          <span :key="'split_'+index" class="nav-split" v-if="index < path.length - 1">/</span>
        </template>
      </p>


      <ul class="project-list row row-4" v-if="project.length > 0">
        <li class="project-item col-item" v-for="(item,index) in getProjectList" :key="index">
          <a class="project" :href="item.name+'/'">
            <div class="project-cover" :style="{backgroundImage:`url('`+item.cover+`')`}"></div>
            <div class="project-info">
              <div class="file-name">{{item.name}}</div>
              <div class="update-time">{{getUpdateTime(item.mtimeMs)}}</div>
            </div>
          </a>
        </li>
      </ul>


      <div class="file-type-wrap type-folder" v-if="folder.length > 0">
        <div class="file-type-name">
          文件夹
        </div>

        <ul class="folder row row-6">
          <li class="col-item" v-for="(item,index) in folder" :key="index">
            <a :href="item.name+'/'" class="folder-item">
              <!-- <i class="folder-icon"></i> -->
              <svg width="188" height="159" viewBox="0 0 188 159" fill="none" xmlns="http://www.w3.org/2000/svg">
                <g filter="url(#filter0_d)">
                  <path
                    d="M38.661 116.318H150C154.418 116.318 158 112.736 158 108.318V33.7879C158 29.3696 154.418 25.7879 150 25.7879H95.1835C92.705 25.7879 90.4202 24.4478 89.2105 22.2846C88.0009 20.1214 85.7161 18.7813 83.2376 18.7813H38.661C34.2427 18.7813 30.661 22.363 30.661 26.7813V108.318C30.661 112.736 34.2428 116.318 38.661 116.318Z"
                    fill="#007EDC" />
                  <rect x="39" y="31" width="110" height="83" rx="6" fill="black" fill-opacity="0.05" />
                  <rect x="40" y="32.8228" width="108" height="82.1833" rx="6" fill="white" />
                  <path
                    d="M150 115.414H38.6611C34.2429 115.414 30.6611 111.833 30.6611 107.414V51C30.6611 46.5817 34.2429 43 38.6611 43H150C154.418 43 158 46.5817 158 51V107.414C158 111.833 154.418 115.414 150 115.414Z"
                    fill="black" fill-opacity="0.06" />
                  <path
                    d="M150 116.318H38.6611C34.2429 116.318 30.6611 112.736 30.6611 108.318V52.8045C30.6611 48.3862 34.2429 44.8045 38.6611 44.8045H150C154.418 44.8045 158 48.3862 158 52.8045V108.318C158 112.736 154.418 116.318 150 116.318Z"
                    fill="#0092FF" />
                  <path
                    d="M150 116.318H38.6611C34.2429 116.318 30.6611 112.736 30.6611 108.318V52.8045C30.6611 48.3862 34.2429 44.8045 38.6611 44.8045H150C154.418 44.8045 158 48.3862 158 52.8045V108.318C158 112.736 154.418 116.318 150 116.318Z"
                    fill="url(#paint0_linear)" fill-opacity="0.1" />
                </g>
                <defs>
                  <filter id="filter0_d" x="0.661133" y="0.781319" width="187.339" height="157.536"
                    filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
                    <feFlood flood-opacity="0" result="BackgroundImageFix" />
                    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
                      result="hardAlpha" />
                    <feOffset dy="12" />
                    <feGaussianBlur stdDeviation="15" />
                    <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" />
                    <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow" />
                    <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape" />
                  </filter>
                  <linearGradient id="paint0_linear" x1="117" y1="36" x2="117" y2="116" gradientUnits="userSpaceOnUse">
                    <stop stop-opacity="0" />
                    <stop offset="1" />
                  </linearGradient>
                </defs>
              </svg>

              <div class="file-name">
                {{item.name}}
              </div>
            </a>
          </li>
        </ul>
      </div>


      <div class="file-type-wrap type-image" v-if="images.length > 0">
        <div class="file-type-name">
          图片
        </div>

        <ul class="images row row-6">
          <li class="col-item" v-for="(item,index) in images" :key="index">
            <a href="javascript:void(0);" class="images-item">
              <div class="image-preview">
                <div class="preview" :style="{backgroundImage:`url('`+current + item.fileName+`')`}">
                  <img @click.ctrl="e => openFile(e,current + item.fileName)" :src="current + item.fileName" />
                </div>
                <!-- <operation :action-list="imgAction" :value="item" @click-item="imageAction"></operation> -->
              </div>

              <div class="file-name">
                {{item.fileName}}
              </div>
            </a>
          </li>
        </ul>
      </div>

      <div class="file-type-wrap type-folder" v-if="otherFiles.length > 0">
        <div class="file-type-name">
          其他文件
        </div>

        <ul class="folder row row-6">
          <li class="col-item" v-for="(item,index) in otherFiles" :key="index">
            <a :href="item.fileName" class="folder-item">
              <!-- <i class="folder-icon"></i> -->
              <svg width="140" height="160" viewBox="0 0 140 160" fill="none" xmlns="http://www.w3.org/2000/svg">
                <g filter="url(#filter0_dd)">
                  <path
                    d="M38 118H102C106.418 118 110 114.418 110 110V41.3137C110 39.192 109.157 37.1571 107.657 35.6569L92.3431 20.3431C90.8429 18.8429 88.808 18 86.6863 18H38C33.5817 18 30 21.5817 30 26V110C30 114.418 33.5817 118 38 118Z"
                    fill="#0190FB" />
                  <path
                    d="M102 117H38C34.134 117 31 113.866 31 110V26C31 22.134 34.134 19 38 19H86.6863C88.5428 19 90.3233 19.7375 91.636 21.0502L106.95 36.364C108.262 37.6767 109 39.4572 109 41.3137V110C109 113.866 105.866 117 102 117Z"
                    stroke="black" stroke-opacity="0.05" stroke-width="2" />
                  <path fill-rule="evenodd" clip-rule="evenodd"
                    d="M73.4142 67.1925C72.6332 66.4114 72.6332 65.1451 73.4142 64.364L78.364 59.4143C79.145 58.6332 80.4113 58.6332 81.1924 59.4143L86.1421 64.364C86.9232 65.1451 86.9232 66.4114 86.1421 67.1925L81.1924 72.1422C80.4113 72.9232 79.145 72.9232 78.364 72.1422L73.4142 67.1925ZM59 60C57.8954 60 57 60.8955 57 62V70C57 71.1046 57.8954 72 59 72H67C68.1046 72 69 71.1046 69 70V62C69 60.8955 68.1046 60 67 60H59ZM59 77.5564C57.8954 77.5564 57 78.4519 57 79.5564V87.5564C57 88.661 57.8954 89.5564 59 89.5564H67C68.1046 89.5564 69 88.661 69 87.5564V79.5564C69 78.4519 68.1046 77.5564 67 77.5564H59ZM75 77.5564C73.8954 77.5564 73 78.4519 73 79.5564V87.5564C73 88.661 73.8954 89.5564 75 89.5564H83C84.1046 89.5564 85 88.661 85 87.5564V79.5564C85 78.4519 84.1046 77.5564 83 77.5564H75Z"
                    fill="#9BD5FF" />
                </g>
                <path
                  d="M92 40H105.586C106.477 40 106.923 38.9229 106.293 38.2929L89.7071 21.7071C89.0771 21.0771 88 21.5233 88 22.4142V36C88 38.2091 89.7909 40 92 40Z"
                  fill="#67BCFD" />
                <defs>
                  <filter id="filter0_dd" x="0" y="0" width="140" height="160" filterUnits="userSpaceOnUse"
                    color-interpolation-filters="sRGB">
                    <feFlood flood-opacity="0" result="BackgroundImageFix" />
                    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
                      result="hardAlpha" />
                    <feOffset dy="12" />
                    <feGaussianBlur stdDeviation="15" />
                    <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" />
                    <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow" />
                    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
                      result="hardAlpha" />
                    <feOffset dy="6" />
                    <feGaussianBlur stdDeviation="5" />
                    <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" />
                    <feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow" />
                    <feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape" />
                  </filter>
                </defs>
              </svg>

              <div class="file-name">
                {{item.fileName}}
              </div>
            </a>
          </li>
        </ul>
      </div>

    </main>
    <footer id="footer">Files preview · <a href="https://gitee.com/Jioho/files-preview">Gitee 地址</a> · <a
        href="https://gitee.com/Jioho/files-preview">关于</a></footer>
  </div>
</body>

<script>
  "use strict";

  function _slicedToArray(arr, i) {
    return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
  }

  function _nonIterableRest() {
    throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
  }

  function _unsupportedIterableToArray(o, minLen) {
    if (!o) return;
    if (typeof o === "string") return _arrayLikeToArray(o, minLen);
    var n = Object.prototype.toString.call(o).slice(8, -1);
    if (n === "Object" && o.constructor) n = o.constructor.name;
    if (n === "Map" || n === "Set") return Array.from(o);
    if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
  }

  function _arrayLikeToArray(arr, len) {
    if (len == null || len > arr.length) len = arr.length;

    for (var i = 0, arr2 = new Array(len); i < len; i++) {
      arr2[i] = arr[i];
    }

    return arr2;
  }

  function _iterableToArrayLimit(arr, i) {
    var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];

    if (_i == null) return;
    var _arr = [];
    var _n = true;
    var _d = false;

    var _s, _e;

    try {
      for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
        _arr.push(_s.value);

        if (i && _arr.length === i) break;
      }
    } catch (err) {
      _d = true;
      _e = err;
    } finally {
      try {
        if (!_n && _i["return"] != null) _i["return"]();
      } finally {
        if (_d) throw _e;
      }
    }

    return _arr;
  }

  function _arrayWithHoles(arr) {
    if (Array.isArray(arr)) return arr;
  }

  var treeData = '{{treeData}}';

  (function () {
    var weeks_ch = ["日", "一", "二", "三", "四", "五", "六"];
    var dateType = "yyyy|y|mm|m|dd|d|hh|h|ii|i|ss|s";
    var imgExt = ['png', 'jpg', 'jpeg', 'svg', 'gif', 'webp', 'bmp', 'tif', 'svga'];
    var coverExt = ['png', 'gif'];
    var beforeTime = [{
      time: 31536000000,
      format: 'YYYY年MM月DD日 更新',
      useDiffTime: false
    }, {
      time: 259200000,
      format: 'MM月DD日 更新',
      useDiffTime: false
    }, {
      time: 86400000,
      format: 'D天前 更新',
      useDiffTime: 'day'
    }, {
      time: 0,
      format: 'h小时前 更新',
      useDiffTime: 'hours'
    }];

    var app = new Vue({
      el: '#app',
      data: {
        project: [],
        folder: [],
        imageObj: [],
        path: [],
        otherFiles: [],
        blurHeder: false,
        current: treeData.current,

        imgAction: [{
          name: '下载文件',
          fn: 'downloadFile'
        }, {
          name: '复制链接',
          fn: 'copyLink'
        }]
      },
      created: function created() {
        this.initFileList();
        this.initFloder();
        this.initNav();
      },
      mounted: function mounted() {
        var _this = this;

        this.$nextTick(function () {
          window.addEventListener("scroll", _this.handleScroll); // 监听（绑定）滚轮滚动事件

          _this.handleScroll();
        });

        this.initViewer()
      },
      beforeDestroy: function beforeDestroy() {
        window.removeEventListener("scroll", this.handleScroll);
      },
      computed: {
        getProjectList: function getProjectList() {
          var _list = this.project || [];

          _list = _list.sort(function (item, item2) {
            return item2.mtimeMs - item.mtimeMs;
          });
          return _list;
        }
      },
      methods: {

        openFile(event, url) {
          event.preventDefault();
          window.open(url, '_blank')
        },

        // 操作图片
        imageAction(actionItem, itemInfo) {
          let fn = actionItem.fn
          if (this[fn]) {
            this[fn](itemInfo)
          }
        },

        downloadFile(itemInfo) {
          window.download(window.location.href + itemInfo.fileName)
        },

        copyLink(itemInfo) {
          var copy = function (e) {
            e.preventDefault()
            e.clipboardData.setData('text/plain', window.location.href + itemInfo.fileName)

            window.VsToast.show({
              title: '复制成功',
              variant: 'success',
              timeout: 3000
            });

            document.removeEventListener('copy', copy)
          }
          document.addEventListener('copy', copy)
          document.execCommand("Copy");
        },

        initViewer() {
          if (window.Viewer) {
            let viewer = new window.Viewer(document.querySelector('.type-image'), {
              inline: false,
              viewed() {
                viewer.zoomTo(1);
              },
            });
          } else {
            setTimeout(() => {
              this.initViewer()
            }, 50)
          }
        },

        getUpdateTime: function getUpdateTime(time) {
          time = parseInt(time);
          var nowTime = new Date().getTime();
          var diffTime = nowTime - time;
          var formatStr = 'YYYY年MM月DD日 更新';
          var useDiffTime = false;
          beforeTime.some(function (item) {
            if (diffTime >= item.time) {
              formatStr = item.format;
              useDiffTime = item.useDiffTime;
              return true;
            }

            return false;
          });

          if (!useDiffTime) {
            return this.formateTime(time, formatStr);
          } else {
            return this.calcTime(diffTime, useDiffTime);
          }
        },
        // 计算相差的时间
        // TODO 支持 format 格式
        calcTime: function calcTime(time) {
          var diffType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'hours'; // 转换时间对象
          // var timeObj = this.timeToDateObj(time)

          switch (diffType) {
            case 'day':
              return parseInt(time / 86400000) + '天前 更新';

            case 'hours':
              return parseInt(time / 3600000) + '小时前 更新';
          }
        },

        /**
         * 格式化时间戳
         * @param  {Number}    time           需要转换的时间戳。默认当前时间
         * @param  {String}    format         时间格式 默认 yyyy-mm-dd HH:ii:ss
         * @return {String}    timeStr        格式化后的字符串
         */
        formateTime: function formateTime() {
          var _this2 = this;

          var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Date().getTime();
          var format = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "yyyy-mm-dd hh:ii:ss";
          time = parseInt(time); // 转换时间对象

          var timeObj = this.timeToDateObj(time); // 正则

          format = format.toLocaleLowerCase().match(new RegExp(dateType + "|.", "g")) || []; // 匹配格式

          format.forEach(function (v, i) {
            if (/yyyy|y/.test(v)) {
              //年
              format[i] = _this2.digit(timeObj.year, v.length);
            } else if (/mm|m/.test(v)) {
              //月
              format[i] = _this2.digit(timeObj.month, v.length);
            } else if (/dd|d/.test(v)) {
              //日
              format[i] = _this2.digit(timeObj.day, v.length);
            } else if (/hh|h/.test(v)) {
              //时
              format[i] = _this2.digit(timeObj.hours, v.length);
            } else if (/ii|i/.test(v)) {
              //分
              format[i] = _this2.digit(timeObj.minutes, v.length);
            } else if (/ss|s/.test(v)) {
              //秒
              format[i] = _this2.digit(timeObj.seconds, v.length);
            }
          });
          return format.join("");
        },

        /**
        * 数字前置补零
        * @param  {Number}  num     传入的数字
        * @param  {Number}  length  需要的长度。默认2位
        * @return {Number}  num     转换好的数字
        */
        digit: function digit(num) {
          var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
          var str = "";
          num = String(num);
          length = length || 2;

          for (var i = num.length; i < length; i++) {
            str += "0";
          }

          return num < Math.pow(10, length) ? str + (num | 0) : num;
        },

        /**
         * 时间戳转换为对象 (new Date()) => (timeObject)
         * @param  {Number}   time   传入原时间
         * @return {Object}   object 转换后的时间戳，和时间对象
         */
        timeToDateObj: function timeToDateObj() {
          var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Date().getTime();
          var date = new Date();
          date.setTime(time);
          return {
            time: time,
            day: date.getDate(),
            month: date.getMonth() + 1,
            year: date.getFullYear(),
            week: weeks_ch[date.getDay()],
            hours: date.getHours(),
            minutes: date.getMinutes(),
            seconds: date.getSeconds(),
            monthStr: this.digit(date.getMonth() + 1),
            dayStr: this.digit(date.getDate()),
            hoursStr: this.digit(date.getHours()),
            minutesStr: this.digit(date.getMinutes()),
            secondsStr: this.digit(date.getSeconds())
          };
        },
        handleScroll: function handleScroll() {
          var scrollHeight = document.documentElement.scrollTop || document.body.scrollTop; //滚动高度

          if (scrollHeight >= 100) {
            this.blurHeder = true;
          } else {
            this.blurHeder = false;
          }
        },
        hasCover: function hasCover(name) {
          var _this3 = this;

          return new Promise(function (resolve, reject) {
            var errorNum = 0;
            coverExt.forEach(function (item) {
              _this3.getCover("".concat(_this3.current).concat(name, "/assets/cover.").concat(item)).then(resolve).catch(function (res) {
                errorNum += 1;

                if (errorNum == coverExt.length) {
                  resolve(false);
                }
              });
            });
          });
        },
        getCover: function getCover(path) {
          return new Promise(function (resolve, reject) {
            if (!path) {
              reject();
            }

            var img = new Image();

            img.onload = function () {
              resolve(path);
            };

            img.onerror = reject;
            img.src = path;
          });
        },
        initNav: function initNav() {
          var routes = treeData.current.split('/');
          var path = '';
          var paths = [];
          routes.forEach(function (item, index) {
            (item || index == 0) && paths.push({
              text: item || 'Home',
              path: path += item + '/'
            });
          });
          this.currentName = paths[paths.length - 1].text;
          this.path = paths;
        },
        initFloder: function initFloder() {
          var _this4 = this;

          var dirs = treeData.dirs; // 项目目录

          var projectList = []; // 普通文件夹

          var folderList = [];
          dirs.forEach(function (item) {
            var _item = _slicedToArray(item, 2),
              name = _item[0],
              dirData = _item[1];

            if (name !== '..') {
              _this4.hasCover(name).then(function (cover) {
                if (cover) {
                  projectList.push({
                    ...dirData,
                    name: name,
                    cover: cover
                  });
                } else {
                  folderList.push({
                    ...dirData,
                    name: name
                  });
                }
              }); // let imgInfo = this.imageObj[name]


              var imgInfo = false;
            }
          });
          this.project = projectList || [];
          this.folder = folderList || [];
        },
        initFileList: function initFileList() {
          var files = treeData.renderFiles;
          var imageObj = {};
          var images = [];
          var otherFiles = [];
          files.forEach(function (item) {
            var fileName = item[0];
            var nameArr = fileName.split('.');
            var ext = nameArr.pop();
            fileName = nameArr.join('.');

            // // feature 当前目录下包含MD文件，自动进入MD视图
            // if (ext === 'md') {
            //   window.location.href += item[0]
            // }

            if (imgExt.indexOf(ext) !== -1) {
              // imageObj[fileName] = {
              //   fileName: item[0],
              //   ...item[1]
              // }
              images.push({
                fileName: item[0],
                ...item[1]
              });
            } else {
              otherFiles.push({
                fileName: item[0],
                ...item[1]
              });
            }
          }); // this.imageObj = imageObj

          this.images = images;
          this.otherFiles = otherFiles;
        }
      }
    });


    Vue.component('operation', {
      template: `
      <div ref="operation" :class="['operation-wrap',{open:open}]" @click.stop="toggleOpen" v-if="actionList.length">
        <div class="operation">
          <div class="dot"></div>
          <div class="dot"></div>
          <div class="dot"></div>
        </div>

        <div :class="['operation-list-wrap',{open:open}]">
          <div class="operation-list">
            <div class="operation-item" v-for="(item,index) in actionList" :key="index" @click.stop="clickItem(item)">{{item.name}}</div>
          </div>
        </div>
      </div>
      `,
      props: {
        actionList: {
          type: Array,
          default: function () {
            return []
          }
        },
        value: {
          type: Object,
          default: function () {
            return {}
          }
        }
      },
      data() {
        return {
          open: false
        }
      },
      mounted() {
        let _this = this
        document.body.addEventListener('click', _this.bodyClick, false)
        app.$on('openOperation', function () {
          _this.bodyClick()
        })
      },
      destroyed() {
        document.body.removeEventListener('click', this.bodyClick, false)
      },
      methods: {
        bodyClick(event) {
          this.open = false
        },
        toggleOpen() {
          app.$emit('openOperation')
          this.$nextTick(() => {
            this.open = !this.open
          })
        },
        clickItem(item) {
          this.$emit('click-item', item, this.value)
          this.open = false
        }
      }
    })
  })();
</script>

<!-- 图片预览服务 -->
<script src="https://cdn.bootcdn.net/ajax/libs/viewerjs/1.10.1/viewer.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/viewerjs/1.10.1/viewer.min.css" rel="stylesheet">

<!-- 下载服务 -->
<script src="https://cdn.bootcdn.net/ajax/libs/downloadjs/1.4.8/download.min.js"></script>

<!-- toast -->
<script src="https://unpkg.com/@vuesimple/vs-toast@1.4.0/dist/vs-toast.min.js"></script>

</html>